package cn.chinaunicom.fast.common.utils;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import lombok.extern.slf4j.Slf4j;

/**
 *
 * @ProjectName: core
 * @Package: com.springboot.cloud.common.core.util
 * @ClassName: MyCache
 * @Description: 简单的项目内缓存，关闭后缓存持久化到本地磁盘
 * @Author: wangjc
 * @CreateDate: 2023-03-30
 * @UpdateUser: 更新者
 * @UpdateDate: 2023-03-30
 * @UpdateRemark: 更新内容
 * @Version: 1.0
 */
@Slf4j
public class MyCache implements Serializable {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 1028946540997236689L;
	/**
	 * 缓存
	 */
	private Map<String, CacheData> cache;
	/**
	 * 缓存失效时间
	 */
	private Map<String, Long> cacheExpires;
	/**
	 * 单例缓存对象
	 */
	private static MyCache singleCache;
	/**
	 * 定时间隔时间(毫秒)
	 */
	public static final long DELAY_TIME = 1000L;
	/**
	 * 永久有效
	 */
	public static final long EXP_ALLTIME = 0L;
	/**
	 * 缓存持久化保存的文件名
	 */
	public static final String CACHE_FILE_NAME = ObjectUtils.getDefaultFilePath() + File.separator + "cstm_tmp.cac";

	/**
	 * 私有话的默认构造函数
	 */
	private MyCache() {
		initCache();
	}

	/**
	 * 获取实例化对象（单例模式）
	 * 
	 * @return
	 */
	public static MyCache getInstance() {
		if (Objects.isNull(singleCache)) {
			synchronized (MyCache.class) {
				boolean isExsist = new File(CACHE_FILE_NAME).exists();
				if (isExsist) {
					singleCache = (MyCache) ObjectUtils.readObj(CACHE_FILE_NAME);
				}
				if (Objects.isNull(singleCache)) {
					singleCache = new MyCache();
				}
				ObjectUtils.getDefaultFilePath();
			}
		}
		return singleCache;
	}

	/**
	 * 获取缓存
	 * 
	 * @param key
	 * @return
	 */
	public Object getCache(String key) {
		CacheData cacheData = cache.get(key);
		return Objects.isNull(cacheData) ? null : cacheData.getData();
	}

	/**
	 * 设置缓存
	 * 
	 * @param key
	 * @param data
	 */
	public void setCache(String key, Object data) {
		cache.put(key, genCacheDate(key, data, EXP_ALLTIME));
	}

	/**
	 * 设置缓存 及 缓存时间(秒)
	 * 
	 * @param key
	 * @param data
	 * @param expires
	 */
	public void setCache(String key, Object data, long expires) {
		if (EXP_ALLTIME == expires) {
			cache.put(key, genCacheDate(key, data, EXP_ALLTIME));
		} else {
			cache.put(key, genCacheDate(key, data, expires));
		}
	}

	/**
	 * 删除缓存
	 * 
	 * @param key
	 */
	public void delCache(String key) {
		cache.remove(key);
	}

	/**
	 * 释放全部缓存
	 */
	public void flushAll() {
		cache.clear();
		cacheExpires.clear();
	}

	/**
	 * 持久化缓存对象
	 */
	public boolean persistence() {
		try {
			if (Objects.nonNull(singleCache)) {
				ObjectUtils.saveObj(CACHE_FILE_NAME, singleCache);
			}
			return true;
		} catch (Exception e) {
			log.error("持久化缓存对象失败：{}");
		}
		return false;
	}

	public int size() {
		return cache.size();
	}

	/**
	 * 根据key的前缀查询所有key
	 * @param keyPatten
	 * @return
	 */
	public List<String> keys(String keyPatten) {
		List<String> keys = new ArrayList<String>();
		cache.forEach((k,v) -> {
			if(k.contains(keyPatten)) {
				log.debug("缓存 key : {}  value : {} ",k,v);
				keys.add(k);
			}
		});
		return keys;
	}

	/**
	 * 当前对象关闭时操作
	 * 
	 * @return
	 */
	public boolean shutdown() {
		return persistence();
	}

	/**
	 * 生成缓存数据的对象
	 */
	private CacheData genCacheDate(String key, Object data, long expires) {
		CacheData cacheData = new CacheData();
		cacheData.setKey(key);
		cacheData.setData(data);
		if (EXP_ALLTIME != expires) {
			long expTime = addDateSeconds((int) expires);
			cacheData.setExpires(expTime);
			cacheExpires.put(key, expTime);
		}
		return cacheData;
	}

	/**
	 * 对日期的【秒】进行加/减
	 * 
	 * @param seconds 秒数, 正数为加,负数为减, 0 则返回当前时间戳
	 * @return 加/减几秒后的日期
	 */
	private long addDateSeconds(int seconds) {
		LocalDateTime localTime = LocalDateTime.now();
		return localTime.plusSeconds(seconds).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
	}

	/**
	 * 对日期的【秒】进行加/减
	 *
	 * @return 加/减几秒后的日期
	 */
	private long getNow() {
		return addDateSeconds(0);
	}

	/**
	 * 缓存初始化
	 */
	private void initCache() {
		cache = Optional.ofNullable(cache).orElse(new Hashtable<>());
		cacheExpires = Optional.ofNullable(cacheExpires).orElse(new Hashtable<>());
	}

	/**
	 * 释放缓存
	 */
	public void releasingCache() {
		cacheExpires.forEach((key, exp) -> {
			if (Objects.nonNull(exp) && getNow() > exp) {
				cache.remove(key);
				exp = EXP_ALLTIME;
			}
		});
//		cache.forEach((key, exp) -> log.debug("缓存key: {}", key));
	}

	/**
	 * 缓存的数据对象
	 */
	class CacheData implements Serializable {

		/**
		 * serialVersionUID
		 */
		private static final long serialVersionUID = -1762769646754312754L;
		/**
		 * 缓存的key
		 */
		private String key;
		/**
		 * 缓存的数据对象
		 */
		private Object data;
		/**
		 * 失效时间戳
		 */
		private long expires;

		/**
		 * @return the key
		 */
		public String getKey() {
			return key;
		}

		/**
		 * @param key the key to set
		 */
		public void setKey(String key) {
			this.key = key;
		}

		/**
		 * @return the data
		 */
		public Object getData() {
			return data;
		}

		/**
		 * @param data the data to set
		 */
		public void setData(Object data) {
			this.data = data;
		}

		/**
		 * @return the expires
		 */
		public long getExpires() {
			return expires;
		}

		/**
		 * @param expires the expires to set
		 */
		public void setExpires(long expires) {
			this.expires = expires;
		}
	}
}
